<script lang="ts" module>
    import { untrack } from "svelte";
    import type { CascaderNode, CascaderProps, CascaderStoreProps } from ".";
    import { TreeCheckMod } from "../Tree/Tree.svelte";
    export type NodeKeyType = string | number;

    export class CascaderStore {
        store: any;
        data: CascaderNode[] = [];
        flatData: CascaderNode[] = [];
        valueField = "value";
        titleField = "title";
        selectedKey: NodeKeyType[];
        value: NodeKeyType[];
        props: CascaderProps;
        mode: TreeCheckMod;
        valMap: Record<NodeKeyType, NodeKeyType[]> = {};

        constructor(store: CascaderStoreProps, props: CascaderProps) {
            this.props = props;
            this.valueField = props.valueField || "value";
            this.titleField = props.titleField || "title";
            this.mode = props.mode ?? TreeCheckMod.HALF;
            this.store = store;
            this.value = $state([]);
            this.selectedKey = $state([]);
            this.init(props.data);
            this.valMap["__"] = this.data.map((item) => item[this.valueField]);

            $effect(() => {
                const data = props.data;
                untrack(() => {
                    if (this.data !== data) {
                        this.init(data);
                        this.valMap["__"] = this.data.map((item) => item[this.valueField]);
                    }
                    const selectedKeys = this.selectedKey;
                    const newVals = selectedKeys?.filter((key) => store.nodeMap[key]);
                    if (newVals?.length !== selectedKeys?.length) {
                        this.selectedKey = newVals;
                    }
                    if (props.multi) {
                        const vals = this.value;
                        const newVals = vals?.filter((key) => store.nodeMap[key]) || [];
                        this.value = newVals;
                    }
                });
            });

            // 外部修改selected值进行同步
            $effect(() => {
                const keys = this.selectedKey;
                const columns = [this.valMap["__"]];

                untrack(() => {
                    if (keys && keys.length) {
                        keys.forEach((key: any) => {
                            if (this.valMap[key]) {
                                columns.push(this.valMap[key]);
                            } else {
                                const item = store.nodeMap[key];
                                if (item && item.children) {
                                    const levelIds = item.children.map((aItem: any) => aItem[this.valueField]);
                                    this.valMap[key] = levelIds;
                                    columns.push(levelIds);
                                }
                            }
                        });
                    }
                });

                store.columns = columns;
            });

            // 外部修改selected值进行同步
            $effect(() => {
                const val = this.value;
                untrack(() => {
                    if (props.multi) {
                        this.setCheckedByMod(val);
                    } else {
                        this.selectedKey = val || [];
                    }
                });
            });
        }

        init(data: any[]) {
            this.data = data;
            this.store.data = data;
            this.flatData = this.getAllFlatNodes(this.store.data);
            Object.keys(this.store.nodeMap).forEach((key) => delete this.store.nodeMap[key]);
            this.store.filteredList = [];
            this.buildRelation(this.store.data, null, 0);
        }

        _buildRelation = (data: CascaderNode[], parent: any, level: number, map: Record<NodeKeyType, CascaderNode>) => {
            data.forEach((item: CascaderNode) => {
                map[item[this.valueField]] = item;
                item._parent = parent;
                item._level = level;
                if (item.children) {
                    this._buildRelation(item.children, item, level + 1, map);
                }
            });
        };

        /**
         * 构建父子关系和层级关系
         * @param data
         * @param parent
         * @param level
         */
        buildRelation = (data: CascaderNode[], parent: any, level: number) => {
            const map: Record<NodeKeyType, CascaderNode> = {};
            this._buildRelation(data, parent, level, map);
            this.store.nodeMap = map;
        };

        /**
         * 获取显示的树节点
         * @param nodes
         * @returns
         */
        getAllFlatNodes = (nodes: CascaderNode[]): CascaderNode[] => {
            const list: CascaderNode[] = nodes.flatMap((item: CascaderNode) => {
                if (item.children?.length) {
                    return [item, this.getAllFlatNodes(item.children)].flat();
                } else {
                    return [item];
                }
            });
            return list;
        };

        getStore() {
            return this.store;
        }

        clearSelect = () => {};

        /**
         * 过滤
         * @param keyword
         */
        filter(keyword: string) {
            if (keyword) {
                const allChildren = this.flatData.filter((item) => !item.children || item.children.length === 0);
                const lines = allChildren.map((item) => {
                    const arr = [];
                    arr.push(item);
                    let parent = item._parent;
                    while (parent) {
                        arr.push(parent);
                        parent = parent._parent;
                    }
                    arr.reverse();
                    return arr;
                });
                const filteredList = lines.filter((line) => {
                    return line.some((item) => item[this.titleField].includes(keyword));
                });

                this.store.filteredList = filteredList;
            } else {
                this.store.filteredList = [];
            }
        }

        getNode = (key: NodeKeyType) => {
            return this.store.nodeMap[key];
        };

        /**
         * 选择节点
         * @param key
         */
        selectItem = (key: NodeKeyType | CascaderNode) => {
            const node = this._getNode(key);
            if (node) {
                const vals = [];
                for (let i = 0; i < node._level!; i++) {
                    vals.push(this.selectedKey[i]);
                }
                vals[node._level!] = node[this.valueField];
                this.selectedKey = vals;
            }
        };

        _getNode = (nodeId: NodeKeyType | CascaderNode) => {
            let node: CascaderNode;
            if (typeof nodeId === "string" || typeof nodeId === "number") {
                node = this.store.nodeMap[nodeId];
            } else {
                node = nodeId as CascaderNode;
                nodeId = node[this.valueField] as NodeKeyType;
            }
            return node;
        };

        /**
         * 更新节点选择状态
         * @param nodeId
         */
        updateNodeCheckStatus = (nodeId: CascaderNode | NodeKeyType | undefined) => {
            if (!nodeId) {
                return;
            }
            const node = this._getNode(nodeId);
            if (node) {
                this.store.nodeMap[node[this.valueField]].checked = this.getNodeChecked(node);
                this.setCheckedForwardUp(node);
            }
        };

        checkNode = (nodeId: CascaderNode | NodeKeyType, checked: boolean) => {
            const node = this._getNode(nodeId);
            if (this.props.beforeChecked) {
                const result = this.props.beforeChecked(node, checked);
                if (result === false) {
                    return;
                }
            }
            const n = this.store.nodeMap[node[this.valueField]];
            if (checked) {
                const addNum = { num: 0 };
                this.willCheckedForwardDownNum(node, checked, addNum);
                this.willCheckedForwardUpNum(node, addNum);
                if (this.props.max && this.value.length + addNum.num + 1 > this.props.max) {
                    this.props.onexceed?.();
                    return;
                }
            }

            n.checked = checked;
            this.setCheckedForwardDown(n, checked);
            n.checked = this.getNodeChecked(n);
            this.setCheckedForwardUp(n);
            const checkedKeys = this.getCheckedKeys(this.mode);
            this.value = checkedKeys;
            this.onValueChange(checkedKeys);
            this.props.onchange && this.props.onchange(checkedKeys);
        };

        onValueChange = (checkedKeys: NodeKeyType[]) => {};

        setCheckedForwardDown = (node: CascaderNode, checked: boolean) => {
            if (node.children) {
                node.children.forEach((item) => {
                    if (item.disabled) return;
                    this.store.nodeMap[item[this.valueField]].checked = checked;
                    this.setCheckedForwardDown(item, checked);
                });
            }
        };

        willCheckedForwardDownNum = (node: CascaderNode, checked: boolean, addNum: { num: number }) => {
            if (node.children) {
                node.children.forEach((item) => {
                    if (item.disabled) return;
                    if (!item.checked) {
                        addNum.num++;
                        this.willCheckedForwardDownNum(item, checked, addNum);
                    }
                });
            }
        };

        getNodeChecked = (nodeId: CascaderNode | NodeKeyType, withoutSelf?: boolean) => {
            const node = this._getNode(nodeId);
            if (!node.children || node.children.length === 0) {
                return node.checked;
            } else {
                let checked: boolean | "indeterminate" = false;
                let checkedNum = 0;
                let indeterminateNum = 0;
                node.children.forEach((item) => {
                    if (item.checked === true) {
                        checkedNum++;
                    }
                    if (item.checked === "indeterminate") {
                        indeterminateNum++;
                    }
                });
                if (checkedNum === node.children.length) {
                    checked = true;
                } else if (checkedNum > 0) {
                    checked = "indeterminate";
                }
                // 假设已选中自身，计算勾选数量使用
                if (withoutSelf) {
                    if (checkedNum + 1 === node.children.length) {
                        checked = true;
                    }
                }
                if (!checked && indeterminateNum > 0) {
                    checked = "indeterminate";
                }
                return checked;
            }
        };

        willCheckedForwardUpNum = (node: CascaderNode, addNum: { num: number }) => {
            const parentNode = node._parent;
            if (parentNode) {
                const checked: boolean | "indeterminate" | undefined = this.getNodeChecked(parentNode, true);
                if (parentNode.checked !== checked) {
                    addNum.num++;
                    this.willCheckedForwardUpNum(parentNode, addNum);
                }
            }
        };

        setCheckedForwardUp = (node: CascaderNode) => {
            const parentNode = node._parent;
            if (parentNode) {
                const checked: boolean | "indeterminate" | undefined = this.getNodeChecked(parentNode);
                this.store.nodeMap[parentNode[this.valueField]].checked = checked;

                this.setCheckedForwardUp(parentNode);
            }
        };

        checkAll = () => {
            for (const key in this.store.nodeMap) {
                this.store.nodeMap[key].checked = true;
            }

            const checkedKeys = this.getCheckedKeys(this.mode);
            this.value = checkedKeys;
            this.props.onchange && this.props.onchange(checkedKeys);
        };

        uncheckAll = () => {
            for (const key in this.store.nodeMap) {
                this.store.nodeMap[key].checked = false;
            }
            const checkedKeys = this.getCheckedKeys(this.mode);
            this.value = checkedKeys;
            this.props.onchange && this.props.onchange(checkedKeys);
        };

        loadData = async (node: CascaderNode, loadDataMethod: (node: CascaderNode) => Promise<CascaderNode[]>) => {
            try {
                const list = await loadDataMethod(node);
                if (list.length > 0) {
                    node.children = list;
                    list.forEach((item) => {
                        this.store.nodeMap[item[this.valueField]] = item;
                    });
                }
            } catch (e) {
                //
            }
            this.store.nodeMap[node[this.valueField]].loading = false;
        };

        /**
         *
         * @param mode
         * @returns
         */
        getChecked = (mode: TreeCheckMod = TreeCheckMod.HALF): CascaderNode[] => {
            if (mode === TreeCheckMod.FULL) {
                return this.getFullChecked();
            }
            if (mode === TreeCheckMod.CHILD) {
                return this.getChildChecked();
            }
            if (mode === TreeCheckMod.HALF) {
                return this.getHalfChecked();
            }
            if (mode === TreeCheckMod.SHALLOW) {
                return this.getShallowChecked();
            }
            return [];
        };

        /**
         * 获取所有选中的节点包含父节点和子节点
         * @returns
         */
        getFullChecked = () => {
            return this.flatData.filter((node: CascaderNode) => this.store.nodeMap[node[this.valueField]].checked === true);
        };

        /**
         * 选中的子节点
         * @returns
         */
        getChildChecked = () => {
            return this.flatData.filter((node: CascaderNode) => this.store.nodeMap[node[this.valueField]].checked === true && (!node.children || node.children.length === 0));
        };

        /**
         * 返回全部选中子节点和部分选中的父节点
         * @returns
         */
        getHalfChecked = () => {
            return this.flatData.filter((node: CascaderNode) => this.store.nodeMap[node[this.valueField]].checked === true || this.store.nodeMap[node[this.valueField]].checked === "indeterminate");
        };

        /**
         * 如果父节点下所有子节点全部选中，只返回父节点
         * @returns
         */
        getShallowChecked = () => {
            const ret: CascaderNode[] = [];
            this.flatData.forEach((node: CascaderNode) => {
                if (node.checked === true) {
                    const parentChecked = (() => {
                        const parent = node._parent;
                        if (!parent) {
                            return false;
                        }
                        return parent.checked === true;
                    })();
                    if (!parentChecked) {
                        ret.push(node);
                    }
                }
            });
            return ret;
        };

        /**
         * 选中的节点标识
         * @param mode
         * @returns
         */
        getCheckedKeys = (mode: TreeCheckMod = TreeCheckMod.HALF): NodeKeyType[] => {
            const nodes = this.getChecked(mode);
            return nodes.map((node: CascaderNode) => node[this.valueField]);
        };

        clearChecked = () => {
            this.flatData.forEach((node: CascaderNode) => {
                this.store.nodeMap[node[this.valueField]].checked = false;
            });
        };

        setCheckedByMod = (val: NodeKeyType[]) => {
            this.clearChecked();
            if (this.mode === TreeCheckMod.FULL) {
                this.setCheckedByFull(val);
            }
            if (this.mode === TreeCheckMod.HALF) {
                this.setCheckedByHalf(val);
            }
            if (this.mode === TreeCheckMod.CHILD) {
                this.setCheckedByChild(val);
            }
            if (this.mode === TreeCheckMod.SHALLOW) {
                this.setCheckedByShallow(val);
            }
        };

        setCheckedByFull = (val: NodeKeyType[]) => {
            val.forEach((key: NodeKeyType) => {
                const n = this.store.nodeMap[key];
                n.checked = true;
                this.setCheckedForwardUp(n);
            });
        };

        setCheckedByHalf = (val: NodeKeyType[]) => {
            val.forEach((key: NodeKeyType) => {
                const node = this._getNode(key);
                if (!node) return;
                if (!node.children || node.children.length === 0) {
                    const n = this.store.nodeMap[key];
                    n.checked = true;
                    this.setCheckedForwardUp(n);
                }
            });
        };

        setCheckedByChild = (val: NodeKeyType[]) => {
            val.forEach((key: NodeKeyType) => {
                const node = this._getNode(key);
                if (!node) return;
                if (!node.children || node.children.length === 0) {
                    const n = this.store.nodeMap[key];
                    n.checked = true;
                    this.setCheckedForwardUp(n);
                }
            });
        };

        setCheckedByShallow = (val: NodeKeyType[]) => {
            val.forEach((key: NodeKeyType) => {
                const n = this.store.nodeMap[key];
                n.checked = true;
                this.setCheckedForwardUp(n);
                this.setCheckedForwardDown(n, true);
            });
        };
    }
</script>
